/*
 * Copyright (c) 2010 Vadim Zhukov <persgray@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef OW_SYSLOGD_H
#define OW_SYSLOGD_H

enum { MAX_MEMBUF	= (256 * 1024) };	/* Maximum memory buffer size */
enum { MAX_MEMBUF_NAME	= 64 };     /* Max length of syslog memorybuffer name */


/*
 * Syslog protocol NB. all numeric fields in network byte order
 *
 * 1. Client fills an sl_cmd structure and sends it to syslogd, then waits for
 *    a reply. Only one request at a time, or deadlock may occur - syslogd uses
 *    blocking I/O.
 * 2. Server fills an sl_reply_hdr structure and sends it back to client.
 * 3. Server sends some lines, all ended with '\n', depending on command issued
 *    (except SL_CMD_FLAGS, see below).
 * 3.1. SL_CMD_LIST: a single-line list of available buffers, each name is
 *      followed by a space. Overflowed buffers are marked by '*' appended
 *      to a name.
 * 3.2. SL_CMD_CLEAR: Single line "Log cleared\n".
 * 3.3. SL_CMD_FLAGS: Nothing sent(!), as info requested belong to sl_reply_hdr.
 * 3.4. SL_CMD_READ_CONT: Send last 10 messages, then send lines continuously.
 * 3.5. Other: Just send message lines as described in (3).
 * 4. If command was not SL_CMD_READ_CONT, syslogd closes the connection.
 * 5. If someone connects to syslogd while it's serving another one, current
 *    connection is dropped.
 */

/* Request */
struct sl_cmd {
	u_int32_t       version;
	u_int32_t	code;
	char		logname[MAX_MEMBUF_NAME];
};

enum { SL_CTL_VERSION = 1 };

enum {
	SL_CMD_READ		= 1,	/* Read out log */
	SL_CMD_READ_CLEAR	= 2,	/* Read and clear log */
	SL_CMD_CLEAR		= 3,	/* Clear log */
	SL_CMD_LIST		= 4,	/* List available logs */
	SL_CMD_FLAGS		= 5,	/* Query flags only */
	SL_CMD_READ_CONT	= 6,	/* Read out log continuously */
};

/* Reply */
struct sl_reply_hdr {
	u_int32_t	version;
	u_int32_t	flags;
        /* Reply text follows, up to MAX_MEMBUF long */
};

enum {
	CTL_HDR_FLAG_OVERFLOW	= 0x01,
};

SLIST_HEAD(logbufferhead, logbuffer);
struct logbuffer {
	SLIST_ENTRY(logbuffer)	 lb_entry;
	int	lb_used;
#define lb_found	lb_used
	int	lb_overflowed;
	char	lb_name[MAX_MEMBUF_NAME];
};

SIMPLEQ_HEAD(sl_taskqueue, sl_task);
struct sl_task {
	SIMPLEQ_ENTRY(sl_task)	entry;
	struct	 sl_cmd cmd;
	struct	 sl_reply_hdr rr;
	char	*result;
	size_t	 phase_progress;
	int	 phase;
	int	 error;			/* errno-code if error occured, or 0 */
};

enum {
	SL_TASK_PHASE_START,		/* waits in queue */
	SL_TASK_PHASE_SEND_CMD,		/* started, sending request */
	SL_TASK_PHASE_RECV_RR,		/* request sent, waiting for reply */
	SL_TASK_PHASE_RECV_DATA,	/* header received, waiting for data */
	SL_TASK_PHASE_DONE,		/* task succeeded or failed */
};

#define sl_task_code(task)	(ntohl((task)->cmd.code))
#define sl_task_done(task)	((task)->phase == SL_TASK_PHASE_DONE)
#define sl_task_succeeded(task)	(sl_task_done(task) && (task)->error == 0)
#define sl_task_failed(task)	(sl_task_done(task) && (task)->error != 0)
#define sl_task_res_size(task)	(task)->phase_progress

enum { SL_MAX_QUEUE_LENGTH = 1000 };

struct sl_context {
	struct sl_taskqueue	 tasks;
	struct logbufferhead	*buffers;    /* Pointer change is atomic */
	struct logbufferhead	*reqbuffers; /* Could be null */
	struct sockaddr_un	 sun;
	size_t			 nbuffers;
	size_t			 qlength;
	int			 logsock;
	int			 has_completed_tasks;
	socklen_t		 addrlen;
};

#define DSLTASK(n, task, text...)	do { \
	if (debug >= n) { \
		fprintf(stderr, text); \
		sl_task_dump(stderr, (task)); \
	} \
} while(0)

extern struct sockaddr_un	sl_sockpath;
#define DEF_SYSLOG_SOCK		"/var/run/syslogd.sock"


int		 sl_init_context(struct sl_context *, struct logbufferhead *);
void		 sl_deinit_context(struct sl_context *);
int		 sl_update_buffers_list(struct sl_context *);
int		 sl_read_and_clear(struct sl_context *sl);
int		 sl_add_task(struct sl_context *, int, const char *);
int		 sl_proceed_tasks(struct sl_context *);
struct sl_task	*sl_pick_done_task(struct sl_context *);
int		 sl_free_task(struct sl_task *);
void		 sl_task_dump(FILE *f, const struct sl_task *);

#endif /* OW_SYSLOGD_H */
